﻿using System.IO;
using System.Reflection;
using System.Web;
using log4net;

namespace TemplateFoundation.Gadgets
{
    /// <summary>
    /// Used to expose embedded resources for gadgets
    /// </summary>
    public class EmbeddedGadgetResourceHandler : IHttpHandler
    {
        private const string REQUEST_PREFIX = "/EmbeddedGadgets/";

        private readonly ILog _logger = LogManager.GetLogger(typeof(EmbeddedGadgetResourceHandler));

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }

        public void ProcessRequest(HttpContext context)
        {
            var virtualPath = context.Request.Url.PathAndQuery;

            _logger.DebugFormat("Handling request for embedded resource '{0}'", virtualPath);

            // Validate the request URL prefix
            if (!virtualPath.StartsWith(REQUEST_PREFIX))
            {
                _logger.ErrorFormat("Invalid embedded resource request, expected path to start with '{0}'", REQUEST_PREFIX);

                WriteFileNotFoundResponse(context.Response);

                return;
            }

            // Strip the URL prefix
            virtualPath = virtualPath.Replace(REQUEST_PREFIX, string.Empty);

            var assemblyName = virtualPath.Substring(0, virtualPath.IndexOf('/'));

            try
            {
                // Load the assembly containing the embedded resource
                var assembly = Assembly.Load(assemblyName);

                if (assembly == null) // Assembly could not be loaded, hence embedded resource not found
                {
                    WriteFileNotFoundResponse(context.Response);

                    return;
                }

                var virtualFile = new GadgetResourceVirtualFile(virtualPath.Substring(virtualPath.IndexOf('/')), assembly);

                if (!virtualFile.FileExists)
                {
                    WriteFileNotFoundResponse(context.Response);

                    return;
                }

                // Write the file to the response
                using (var fs = virtualFile.Open())
                {
                    var reader = new BinaryReader(fs);

                    context.Response.Clear();
                    context.Response.StatusCode = 200;
                    context.Response.ContentType = GetContentType(virtualPath);
                    context.Response.BinaryWrite(reader.ReadBytes((int)fs.Length));
                    context.Response.End();
                }

            }
            catch (FileNotFoundException)
            {
                // Assembly not found, hence embedded resource not found
                WriteFileNotFoundResponse(context.Response);

                return;
            }
        }

        private static void WriteFileNotFoundResponse(HttpResponse response)
        {
            response.Clear();
            response.StatusCode = 404;
            response.End();
        }

        private static string GetContentType(string path)
        {
            switch (Path.GetExtension(path).ToLower())
            {
                case ".bmp": 
                    return "image/bmp";
                case ".gif": 
                    return "image/gif";
                case ".jpg":
                case ".jpeg": 
                    return "image/jpeg";
                case ".png": 
                    return "image/png";
                case ".css":
                    return "text/css";
                case ".htm":
                case ".html":
                    return "text/css";
                case ".js":
                    return "text/javascript";
                default: // All other file types are served as downloadable files
                    return "application/octet-stream";
            }
        }
    }
}